home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 1.toast / pc / sample code / contributed / waste / demo / source / wedemowindows.c < prev   
Encoding:
C/C++ Source or Header  |  2000-06-23  |  27.3 KB  |  1,154 lines

  1. /*
  2.     WASTE Demo Project:
  3.     Window Handling
  4.  
  5.     Copyright © 1993-1998 Marco Piovanelli
  6.     All Rights Reserved
  7.  
  8.     C port by John C. Daub
  9. */
  10.  
  11.  
  12. #ifndef __ALIASES__
  13. #include <Aliases.h>
  14. #endif
  15.  
  16. #ifndef __ERRORS__
  17. #include <Errors.h>
  18. #endif
  19.  
  20. #ifndef __FIXMATH__
  21. #include <FixMath.h>
  22. #endif
  23.  
  24. #ifndef __TOOLUTILS__
  25. #include <ToolUtils.h>
  26. #endif
  27.  
  28. #ifndef __FILETYPESANDCREATORS__
  29. #include <FileTypesAndCreators.h>
  30. #endif
  31.  
  32. #ifndef _LongCoords_
  33. #include "LongCoords.h"
  34. #endif
  35.  
  36. #ifndef __WEDEMOAPP__
  37. #include "WEDemoIntf.h"
  38. #endif
  39.  
  40. #ifndef __SMARTSCROLLAPI__
  41. #include "SmartScroll.h"
  42. #endif
  43.  
  44. // some consts used by DoGrow()
  45.  
  46. enum {
  47.     kMinWindowWidth        = 200,
  48.     kMinWindowHeight    = 80
  49. };
  50.  
  51. // static variables
  52.  
  53. static SInt32 sScrollStep; // how many pixels to scroll (used by ScrollProc)
  54.  
  55.  
  56. static void    CalcGrowIconRect( WindowPtr window, Rect *iconRect )
  57. {
  58.     Rect portRect = window -> portRect ;
  59.  
  60.     iconRect->top = portRect.bottom - (kBarWidth - 2);
  61.     iconRect->left = portRect.right - (kBarWidth - 2);
  62.     iconRect->bottom = portRect.bottom;
  63.     iconRect->right = portRect.right;
  64. }
  65.  
  66. static void    CalcTextRect( WindowPtr window, Rect *textRect )
  67. {
  68.     Rect portRect = window -> portRect ;
  69.  
  70.     textRect->top = 0;
  71.     textRect->left = 0;
  72.     textRect->bottom = portRect.bottom - (kBarWidth - 1);
  73.     textRect->right = portRect.right - (kBarWidth - 1);
  74.     InsetRect( textRect, kTextMargin, kTextMargin );
  75. }
  76.  
  77. static void    CalcScrollBarRect( WindowPtr window, Orientation orientation, Rect *barRect )
  78. {
  79.     Rect portRect = window -> portRect ;
  80.  
  81.     switch ( orientation )
  82.     {
  83.         case kVertical :
  84.         {
  85.             barRect->top = -1;
  86.             barRect->left = portRect.right - (kBarWidth - 1);
  87.             barRect->bottom = portRect.bottom - (kBarWidth - 2);
  88.             barRect->right = portRect.right + 1;
  89.             break;
  90.         }
  91.  
  92.         case kHorizontal :
  93.         {
  94.             barRect->top = portRect.bottom - (kBarWidth - 1);
  95.             barRect->left = -1;
  96.             barRect->bottom = portRect.bottom + 1;
  97.             barRect->right = portRect.right - (kBarWidth - 2 );
  98.             break;
  99.         }
  100.     }
  101. }
  102.  
  103. /*
  104.     the standard Toolbox trap _DrawGrowIcon draws two lines from the grow icon
  105.     to the left and top margins of the window's content area
  106.     these additional lines may create ugly dirt, so we use this routine to temporarily
  107.     set the clip region to the grow icon rect.
  108.  
  109.     in addition, if validate is true, we call _ValidRect on the icon rect
  110. */
  111.  
  112. static void    MyDrawGrowIcon( WindowPtr window, Boolean validate )
  113. {
  114.     GrafPtr        savePort;
  115.     RgnHandle    saveClip;
  116.     Rect        r;
  117.  
  118.     // save port and set thePort to wind
  119.  
  120.     GetPort( &savePort );
  121.     SetPort( window );
  122.  
  123.     // save the clip region
  124.  
  125.     saveClip = NewRgn();
  126.     GetClip( saveClip );
  127.  
  128.     // calculate the grow icon rect
  129.  
  130.     CalcGrowIconRect( window, &r );
  131.  
  132.     // set clip region to grow icon rect
  133.  
  134.     ClipRect( &r );
  135.  
  136.     // call _DrawGrowIcon
  137.  
  138.     DrawGrowIcon( window );
  139.  
  140.     // if validate is true, remove the grow icon rect from the update region
  141.  
  142.     if ( validate )
  143.         ValidRect( &r );
  144.  
  145.     // restore old clip region
  146.  
  147.     SetClip( saveClip );
  148.     DisposeRgn( saveClip );
  149.  
  150.     // restore old port
  151.  
  152.     SetPort( savePort );
  153. }
  154.  
  155. static void    ScrollBarChanged( WindowPtr window )
  156. {
  157.     // scroll text to reflect new scroll bar setting
  158.  
  159.     DocumentHandle hDocument = GetWindowDocument( window );
  160.     WEReference    we;
  161.     LongRect viewRect, destRect;
  162.  
  163.     we = (*hDocument)->we;
  164.     WEGetViewRect( &viewRect, we );
  165.     WEGetDestRect( &destRect, we );
  166.     WEScroll
  167.     (
  168.         viewRect.left - destRect.left - LCGetValue( (*hDocument)->scrollBars[ kHorizontal ] ),
  169.         viewRect.top - destRect.top - LCGetValue( (*hDocument)->scrollBars[ kVertical ] ),
  170.         we
  171.     );
  172. }
  173.  
  174. static void    AdjustBars ( WindowPtr window )
  175. {
  176.     DocumentHandle    hDocument ;
  177.     WEReference        we ;
  178.     GrafPtr            savePort ;
  179.     LongRect        viewRect, destRect ;
  180.     SInt32            visible, total, value, max ;
  181.     ControlHandle    bar ;
  182.  
  183.     GetPort ( & savePort ) ;
  184.     SetPort ( window ) ;
  185.  
  186.     hDocument = GetWindowDocument ( window ) ;
  187.     we = ( * hDocument ) -> we ;
  188.  
  189.     // get the view and destination rectangle
  190.     WEGetViewRect ( & viewRect, we ) ;
  191.     WEGetDestRect ( & destRect, we ) ;
  192.  
  193.     //    do the vertical axis
  194.  
  195.     //    get scroll bar handle
  196.     bar = ( * hDocument ) -> scrollBars [ kVertical ] ;
  197.  
  198.     //    calculate new scroll bar settings
  199.  
  200.     //    NOTE:  (destRect.bottom - destRect.top) always equals the total text height because
  201.     //    WASTE automatically updates destRect.bottom whenever line breaks are recalculated
  202.  
  203.     total = destRect . bottom - destRect . top ;    //    total pixel height
  204.     visible = viewRect . bottom - viewRect . top ;    //    visible pixel height
  205.     max = total - visible ;                            //    scrollable range (in pixels)
  206.     value = viewRect . top - destRect . top ;        //    thumb location within scrollable range
  207.  
  208.     //    make sure max is always non-negative
  209.     if ( max <= 0 ) max = 0 ;
  210.  
  211.     //    notify SmartScroll
  212.     SetSmartScrollInfo ( bar, visible, total ) ;
  213.  
  214.     //    reset the scroll bar
  215.     LCSetMax ( bar, max ) ;
  216.     LCSetValue ( bar, value ) ;
  217.  
  218.     //    if value exceeds max then the bottom of the destRect is above
  219.     //    the bottom of the view rectangle:  we need to scroll the text downward
  220.     if ( value > max )
  221.     {
  222.         ScrollBarChanged ( window ) ;
  223.     }
  224.  
  225.     //    now do the horizontal axis
  226.  
  227.     //    get scroll bar handle
  228.     bar = ( * hDocument ) -> scrollBars [ kHorizontal ] ;
  229.  
  230.     //    calculate new scroll bar settings
  231.     total = destRect . right - destRect . left ;    //    total pixel width
  232.     visible = viewRect . right - viewRect . left ;    //    visible pixel width
  233.     max = total - visible ;                            //    scrollable range (in pixels)
  234.     value = viewRect . left - destRect . left ;        //    thumb location within scrollable range
  235.  
  236.     //    make sure max is always non-negative
  237.     if ( max <= 0 ) max = 0 ;
  238.  
  239.     //    notify SmartScroll
  240.     SetSmartScrollInfo ( bar, visible, total ) ;
  241.  
  242.     //    reset the scroll bar
  243.     LCSetMax ( bar, max ) ;
  244.     LCSetValue ( bar, value ) ;
  245.  
  246.     SetPort ( savePort ) ;
  247. }
  248.  
  249. static void    ViewChanged( WindowPtr window )
  250. {
  251.     DocumentHandle    hDocument;
  252.     GrafPtr            savePort;
  253.     ControlHandle    bar;
  254.     Rect            r;
  255.     LongRect        viewRect;
  256.     Orientation        orientation;
  257.  
  258.     GetPort( &savePort );
  259.     SetPort( window );
  260.  
  261.     hDocument = GetWindowDocument( window );
  262.  
  263.     //    resize the text area
  264.  
  265.     CalcTextRect( window, &r );
  266.     WERectToLongRect( &r, &viewRect );
  267.     WESetViewRect( &viewRect, (*hDocument)->we );
  268.  
  269.     //     move and resize the control bars
  270.  
  271.     for ( orientation = kVertical; orientation <= kHorizontal; orientation++ )
  272.     {
  273.         bar = (*hDocument)->scrollBars[ orientation ];
  274.         CalcScrollBarRect( window, orientation, &r );
  275.         MoveControl( bar, r.left, r.top );
  276.         SizeControl( bar, r.right - r.left, r.bottom - r.top );
  277.         ValidRect( &r );
  278.     }
  279.  
  280.     //    reset the thumb positions and the max values of the control bars
  281.     AdjustBars( window );
  282.  
  283.     //    redraw the control bars
  284.  
  285.     ShowControl( (*hDocument)->scrollBars[ kVertical ] );
  286.     ShowControl( (*hDocument)->scrollBars[ kHorizontal ] );
  287.  
  288.     SetPort( savePort );
  289. }
  290.  
  291. /*
  292.     This is a deviation from the original Pascal WASTE Demo App code.
  293.  
  294.     This "morally correct" code for window dragging is per an article in MacTech
  295.     Magazine (July 1994, Vol 10, No. 7). by Eric Shapiro (of Rock Ridge Enterprises)
  296.     called "Multiple Monitors vs. Your Application"
  297.  
  298.     Eric addressed numerous things to allow your app to deal nicely with multiple
  299.     monitor setups, one of them is dragging.
  300.  
  301.     According to Eric, many apps don't let you drag windows to second monitors, and
  302.     though holding down the cmd/opt keys often overrides this problem, it should
  303.     still be updated.  And the only reason qd.screenBits.bounds works to allow
  304.     you to drag to second monitors is because of a kludge Apple put in the Window Manager
  305.  
  306.     So, this is some code from Eric to make our app be "morally correct" :)
  307. */
  308.  
  309. void DoDrag ( Point thePoint, WindowPtr window )
  310. {
  311.     Rect desktopBounds ;
  312.  
  313.     if ( gHasColorQD )
  314.     {
  315.         desktopBounds = ( * GetGrayRgn ( ) ) -> rgnBBox ;
  316.     }
  317.     else
  318.     {
  319.         desktopBounds = qd . screenBits . bounds ;
  320.     }
  321.  
  322.     DragWindow ( window, thePoint, & desktopBounds ) ;
  323. }
  324.  
  325. void Resize ( Point newSize, WindowPtr window )
  326. {
  327.     DocumentHandle    hDocument ;
  328.     GrafPtr            savePort ;
  329.     Rect            r ;
  330.     RgnHandle        tempRgn, dirtyRgn ;
  331.  
  332.     //    set up the port
  333.     GetPort( & savePort ) ;
  334.     SetPort ( window ) ;
  335.  
  336.     hDocument = GetWindowDocument ( window ) ;
  337.  
  338.     //    create temporary regions for calculations
  339.     tempRgn = NewRgn ( ) ;
  340.     dirtyRgn = NewRgn ( ) ;
  341.  
  342.     //    save old text region
  343.     CalcTextRect ( window, & r ) ;
  344.     RectRgn ( tempRgn, & r ) ;
  345.  
  346.     //    erase the old grow icon rect
  347.     CalcGrowIconRect ( window, & r ) ;
  348.     EraseRect ( & r ) ;
  349.  
  350.     //    hide the scroll bars
  351.     HideControl ( ( * hDocument ) -> scrollBars [ kVertical ] ) ;
  352.     HideControl ( ( * hDocument ) -> scrollBars [ kHorizontal ] ) ;
  353.  
  354.     //    perform the actual resizing of the window, redraw scroll bars and grow icon
  355.     SizeWindow ( window, newSize . h, newSize . v, false ) ;
  356.     ViewChanged ( window ) ;
  357.     MyDrawGrowIcon ( window, true ) ;
  358.  
  359.     //    calculate the dirty region (to be updated)
  360.     CalcTextRect ( window, & r );
  361.     RectRgn ( dirtyRgn, & r ) ;
  362.     XorRgn ( dirtyRgn, tempRgn, dirtyRgn ) ;
  363.     InsetRect ( & r, - kTextMargin, - kTextMargin ) ;
  364.     RectRgn ( tempRgn, & r ) ;
  365.     SectRgn ( dirtyRgn, tempRgn, dirtyRgn ) ;
  366.  
  367.     //    mark the dirty region as invalid
  368.     InvalRgn ( dirtyRgn ) ;
  369.  
  370.     //    throw away temporary regions
  371.     DisposeRgn ( tempRgn ) ;
  372.     DisposeRgn ( dirtyRgn ) ;
  373.  
  374.     //    restore the port
  375.     SetPort ( savePort ) ;
  376. }
  377.  
  378. void DoGrow ( Point hitPt, WindowPtr window )
  379. {
  380.     Rect sizeRect ;
  381.     SInt32 newSize ;
  382.  
  383.     SetRect( & sizeRect, kMinWindowWidth, kMinWindowHeight, SHRT_MAX, SHRT_MAX ) ;
  384.     if ( ( newSize = GrowWindow ( window, hitPt, & sizeRect ) ) != 0L )
  385.     {
  386.         //    for some reason, GrowWindow( ) returns a long value,
  387.         //    but it's really a Point
  388.  
  389.         Resize ( * ( Point * ) & newSize, window ) ;
  390.     }
  391. }
  392.  
  393. void DoZoom ( SInt16 partCode, WindowPtr window )
  394. {
  395.     DocumentHandle    hDocument;
  396.     GrafPtr            savePort;
  397.     Rect            r;
  398.  
  399.     GetPort( &savePort );
  400.     SetPort( window );
  401.  
  402.     hDocument = GetWindowDocument(window);
  403.  
  404.     r = window -> portRect ;
  405.     EraseRect( &r );
  406.     HideControl( (*hDocument)->scrollBars[ kVertical ] );
  407.     HideControl( (*hDocument)->scrollBars[ kHorizontal ] );
  408.  
  409.     ZoomWindow( window, partCode, false );
  410.  
  411.     ViewChanged( window );
  412.     CalcTextRect( window, &r );
  413.     InvalRect( &r );
  414.  
  415.     SetPort( savePort );
  416. }
  417.  
  418. // this is a callback tourine called by the Toolbox Control Manager
  419. // move the scroll bar thumb and scroll the text accordingly
  420.  
  421. static pascal void ScrollProc ( ControlHandle bar, ControlPartCode partCode )
  422. {
  423.     SInt32 value, step ;
  424.  
  425.     if ( partCode == kControlNoPart )
  426.     {
  427.         return ;
  428.     }
  429.  
  430.     value = LCGetValue ( bar ) ;
  431.     step = sScrollStep ;
  432.  
  433.     if ( ( ( value < LCGetMax ( bar ) ) && ( step > 0 ) ) ||
  434.          ( ( value > 0 ) && ( step < 0 ) ) )
  435.     {
  436.         LCSetValue ( bar, value + step ) ;
  437.         ScrollBarChanged ( FrontWindow ( ) ) ;
  438.     }
  439. }
  440.  
  441. static SInt32 MySendControlMessage ( ControlHandle inControl, SInt16 inMessage, SInt32 inParam )
  442. {
  443.     GrafPtr savePort ;
  444.     Handle cdef ;
  445.     SInt32 result ;
  446.     SInt8 saveState ;
  447.  
  448.     //    get a handle to the control definition procedure
  449.     cdef = ( * inControl ) -> contrlDefProc ;
  450.  
  451.     //    make sure the CDEF is loaded
  452.     if ( * cdef == nil )
  453.     {
  454.         LoadResource ( cdef ) ;
  455.         if ( * cdef == nil )
  456.         {
  457.             return 0 ;        //    emergency exit (couldn't load CDEF)
  458.         }
  459.     }
  460.  
  461.     //    lock it down
  462.     saveState = HGetState ( cdef ) ;
  463.     HLock ( cdef ) ;
  464.  
  465.     //    set up the port
  466.     GetPort ( & savePort ) ;
  467.     SetPort ( ( * inControl ) -> contrlOwner ) ;
  468.  
  469.     //    call the CDEF
  470.     result = CallControlDefProc ( ( ControlDefUPP ) StripAddress ( * cdef ),
  471.                 GetControlVariant ( inControl ), inControl, inMessage, inParam ) ;
  472.  
  473.     //    unlock the CDEF
  474.     HSetState ( cdef, saveState ) ;
  475.  
  476.     //    restore the port
  477.     SetPort ( savePort ) ;
  478.  
  479.     //    return result code
  480.     return result ;
  481. }
  482.  
  483. static void LiveScroll ( ControlHandle inControl, Point inHitPt, WindowPtr inWindow )
  484. {
  485.     IndicatorDragConstraint constraint ;
  486.     Point mouseLoc ;
  487.     SInt32 initialValue, oldValue, curValue, max ;
  488.     SInt16 scrollRange, delta ;
  489.     Orientation orientation ;
  490.  
  491.     //    hilite the control thumb
  492.     //    this does nothing with the standard System 7.x scroll bar, but is required for
  493.     //    correct visual feedback with the Apple Grayscale Appearance (as implemented by
  494.     //    the Appearance control panel in MacOS 8, or by Aaron/Kaleidoscope)
  495.     HiliteControl ( inControl, kControlIndicatorPart ) ;
  496.  
  497.     //    get limit & slop rects that should be used for dragging the indicator
  498.     //    (see IM: Mac Toolbox Essentials, page 5-114)
  499.     * ( Point * ) & constraint . limitRect = inHitPt ;
  500.     MySendControlMessage ( inControl, thumbCntl, ( SInt32 ) & constraint ) ;
  501.  
  502.     //    determine the orientation of the scroll bar
  503.     orientation = ( constraint . axis == kVerticalConstraint ) ? kHorizontal : kVertical ;
  504.  
  505.     //    calculate the area in which the thumb can travel
  506.     if ( orientation == kVertical )
  507.     {
  508.         scrollRange = ( constraint . limitRect . bottom - constraint . limitRect . top ) ;
  509.     }
  510.     else
  511.     {
  512.         scrollRange = ( constraint . limitRect . right - constraint . limitRect . left ) ;
  513.     }
  514.  
  515.     //    get current value & max
  516.     initialValue = oldValue = curValue = LCGetValue ( inControl ) ;
  517.     max = LCGetMax ( inControl ) ;
  518.  
  519.     //    mouse tracking loop
  520.     while ( StillDown ( ) )
  521.     {
  522.         //    get current mouse location
  523.         GetMouse ( & mouseLoc ) ;
  524.  
  525.         //    do nothing if the mouse is outside the slop rectangle
  526.         if ( PtInRect ( mouseLoc, & constraint . slopRect ) )
  527.         {
  528.             //    calculate pixel offset relative to initial hit point
  529.             if ( orientation == kVertical )
  530.             {
  531.                 delta = mouseLoc . v - inHitPt . v ;
  532.             }
  533.             else
  534.             {
  535.                 delta = mouseLoc . h - inHitPt . h ;
  536.             }
  537.  
  538.             //    calculate new control value
  539.             curValue = initialValue + FixMul ( max, FixRatio ( delta, scrollRange ) ) ;
  540.             if ( curValue < 0 ) curValue = 0 ;
  541.             if ( curValue > max ) curValue = max ;
  542.         }
  543.  
  544.         if ( curValue != oldValue )
  545.         {
  546.             //    set new control value
  547.             LCSetValue ( inControl, curValue ) ;
  548.             ScrollBarChanged ( inWindow ) ;
  549.             oldValue = curValue ;
  550.         }
  551.     }
  552.  
  553.     //    unhighlight the thumb
  554.     HiliteControl ( inControl, kControlNoPart ) ;
  555. }
  556.  
  557. static void    DoScrollBar ( Point hitPt, EventModifiers modifiers, WindowPtr window )
  558. {
  559.     DocumentHandle        hDocument;
  560.     ControlHandle        bar = nil;
  561.     LongRect            viewRect;
  562.     ControlPartCode        partCode;
  563.     SInt32                pageSize;
  564.     SInt32                step = 0;
  565.  
  566. #ifdef __cplusplus
  567.     static ControlActionUPP sScrollerUPP = NewControlActionProc( ScrollProc );
  568. #else
  569.     static ControlActionUPP sScrollerUPP = nil;
  570.     if (sScrollerUPP == nil)
  571.     {
  572.         sScrollerUPP = NewControlActionProc( ScrollProc );
  573.     }
  574. #endif
  575.  
  576.     hDocument = GetWindowDocument( window );
  577.     WEGetViewRect( &viewRect, (*hDocument)->we );
  578.  
  579.     //    find out which control was hit (if any) and in which part
  580.     partCode = FindControl( hitPt, window, &bar );
  581.  
  582.     //    if any control was hit, it must be one of our two scroll bars:
  583.     //    find out which and calculate the page size for it
  584.     if ( bar == (*hDocument)->scrollBars[ kVertical ] )
  585.     {
  586.         pageSize = viewRect.bottom - viewRect.top;
  587.     }
  588.     else if ( bar == (*hDocument)->scrollBars[ kHorizontal ] )
  589.     {
  590.         pageSize = viewRect.right - viewRect.left;
  591.     }
  592.     else
  593.     {
  594.         return;        // return immediately if none of our scrollbars was hit
  595.     }
  596.  
  597.     //    dispatch on partCode
  598.     switch ( partCode )
  599.     {
  600.         case kControlIndicatorPart:
  601.         {
  602.             // click in thumb
  603.             if ( modifiers & optionKey )
  604.             {
  605.                 // call TrackControl with no actionProc and adjust text
  606.                 TrackControl ( bar, hitPt, nil ) ;
  607.                 LCSynch ( bar ) ;
  608.                 ScrollBarChanged ( window ) ;
  609.             }
  610.             else
  611.             {
  612.                 LiveScroll ( bar, hitPt, window ) ;
  613.             }
  614.             return;
  615.         }
  616.  
  617.         case kControlUpButtonPart:
  618.         {
  619.             step = - ( ( modifiers & optionKey ) ? 1 : kScrollDelta ) ;
  620.             break ;
  621.         }
  622.  
  623.         case kControlDownButtonPart:
  624.         {
  625.             step = + ( ( modifiers & optionKey ) ? 1 : kScrollDelta ) ;
  626.             break ;
  627.         }
  628.  
  629.         case kControlPageUpPart:
  630.         {
  631.             step = - ( pageSize - kScrollDelta ) ;
  632.             break ;
  633.         }
  634.  
  635.         case kControlPageDownPart:
  636.         {
  637.             step = + ( pageSize - kScrollDelta ) ;
  638.             break ;
  639.         }
  640.  
  641.     }    // switch
  642.  
  643.     //    save step in a static variable for our ScrollProc callback
  644.     sScrollStep = step ;
  645.  
  646.     //    track the mouse
  647.     TrackControl ( bar, hitPt, sScrollerUPP ) ;
  648. }
  649.  
  650. /*
  651.  *    TextScrolled
  652.  *
  653.  *    This is a callback routine called whenever the text is scrolled automatically.
  654.  *    Since auto-scrolling is enabled, WEScroll may be invoked internally by WASTE
  655.  *    in many different circumstances, and we want to be notified when this happens
  656.  *    so we can adjust the scroll bars
  657.  */
  658.  
  659. static pascal void TextScrolled ( WEReference we )
  660. {
  661.     WindowPtr window = nil ;
  662.  
  663.     //    retrieve the window pointer stored in the WE instance as a "reference constant"
  664.     if ( WEGetInfo( weRefCon, & window, we ) != noErr )
  665.     {
  666.         return ;
  667.     }
  668.  
  669.     //    make sure the scroll bars are in synch with the destination rectangle
  670.     AdjustBars ( window ) ;
  671. }
  672.  
  673. static pascal OSErr AddClippingName ( DragReference drag, WEReference we )
  674. {
  675.     //    add a 'clnm' flavor containing the name of the document originating the drag
  676.     //    this flavor is used by the Finder (version 8.0 and later) to determine the
  677.     //    name of the clipping file
  678.     WindowPtr window = nil ;
  679.     Str255 windowTitle ;
  680.  
  681.     //    retrieve the window pointer stored in the WE instance as a "reference constant"
  682.     if ( WEGetInfo ( weRefCon, & window, we ) != noErr )
  683.     {
  684.         return paramErr ;
  685.     }
  686.  
  687.     //    get the window title
  688.     GetWTitle ( window, windowTitle ) ;
  689.  
  690.     //    put the window title into the drag, as a 'clnm' flavor
  691.     //    we add this flavor to the same drag item used by WASTE to add the TEXT
  692.     //    (note that the reference number of this drag item = the WEReference )
  693.     return AddDragItemFlavor ( drag, ( ItemReference ) we, 'clnm', windowTitle,
  694.         StrLength ( windowTitle ) + 1, flavorNotSaved ) ;
  695. }
  696.  
  697. Boolean    DoContent ( Point hitPt, const EventRecord * event, WindowPtr window )
  698. {
  699.     WEReference        we = GetWindowWE ( window ) ;
  700.     Rect            textRect ;
  701.     GrafPtr            savePort ;
  702.     Boolean            isMyClick = false ;
  703.  
  704.     //    set up the port
  705.     GetPort ( & savePort ) ;
  706.     SetPort ( window ) ;
  707.  
  708.     //    convert the point to local coordinates
  709.     GlobalToLocal ( & hitPt ) ;
  710.  
  711.     //    a click in an inactive window should normally activate it,
  712.     //    but the availability of the Drag Manager introduces an exception to this rule:
  713.     //    a click in the background selection may start a drag gesture,
  714.     //    without activating the window
  715.  
  716.     if ( IsWindowHilited ( window ) )
  717.     {
  718.         isMyClick = true ;            //    active window -> always handle click
  719.     }
  720.     else if ( gHasDragAndDrop )
  721.     {
  722.         SInt32 selStart, selEnd ;
  723.         RgnHandle selRgn ;
  724.  
  725.         WEGetSelection ( & selStart, & selEnd, we ) ;
  726.         selRgn = WEGetHiliteRgn ( selStart, selEnd, we ) ;
  727.         isMyClick = PtInRgn ( hitPt, selRgn ) && WaitMouseMoved ( event -> where ) ;
  728.         DisposeRgn ( selRgn ) ;
  729.     }
  730.  
  731.     if ( isMyClick )
  732.     {
  733.         CalcTextRect ( window, & textRect ) ;
  734.  
  735.         if ( PtInRect ( hitPt, & textRect ) )
  736.         {
  737.             WEClick ( hitPt, event -> modifiers, event -> when, we ) ;
  738.         }
  739.         else
  740.         {
  741.             DoScrollBar ( hitPt, event -> modifiers, window ) ;
  742.         }
  743.     }
  744.  
  745.     //    restore the port
  746.     SetPort ( savePort ) ;
  747.  
  748.     //    return true if the click should activate this window
  749.     return ! isMyClick ;
  750. }
  751.  
  752. static void    DoScrollKey ( SInt16 keyCode, WindowPtr window )
  753. {
  754.     DocumentHandle        hDocument ;
  755.     ControlHandle        bar ;
  756.     SInt32                value ;
  757.     LongRect            viewRect ;
  758.  
  759.     hDocument = GetWindowDocument ( window ) ;
  760.     bar = ( * hDocument ) -> scrollBars [ kVertical ] ;
  761.  
  762.     //    get current scroll bar value
  763.     value = LCGetValue ( bar ) ;
  764.  
  765.     //    get text view rect
  766.     WEGetViewRect ( & viewRect, ( * hDocument ) -> we ) ;
  767.  
  768.     switch ( keyCode )
  769.     {
  770.  
  771.         case keyPgUp:
  772.         {
  773.             value -= ( viewRect . bottom - viewRect . top ) - kScrollDelta ;
  774.             break ;
  775.         }
  776.  
  777.         case keyPgDn:
  778.         {
  779.             value += ( viewRect . bottom - viewRect . top ) - kScrollDelta ;
  780.             break ;
  781.         }
  782.  
  783.         case keyHome:
  784.         {
  785.             value = 0 ;
  786.             break ;
  787.         }
  788.  
  789.         case keyEnd:
  790.         {
  791.             value = LONG_MAX ;
  792.             break ;
  793.         }
  794.     }    // switch
  795.  
  796.     //    set the new scroll bar value and scroll the text pane accordingly
  797.  
  798.     LCSetValue ( bar, value ) ;
  799.     ScrollBarChanged ( window ) ;
  800. }
  801.  
  802. void DoKey ( SInt16 key, const EventRecord * event )
  803. {
  804.     WindowPtr window ;
  805.     SInt16 keyCode ;
  806.  
  807.     //    do nothing if no window is active
  808.     if ( ( window = FrontWindow ( ) ) == nil )
  809.         return;
  810.  
  811.     //    extract virtual key code from event record
  812.     keyCode = ( event->message & keyCodeMask ) >> 8 ;
  813.  
  814.     // page movement keys are handled by DoScrollKey()
  815.     switch ( keyCode )
  816.     {
  817.         case keyPgUp:
  818.         case keyPgDn:
  819.         case keyHome:
  820.         case keyEnd:
  821.         {
  822.             DoScrollKey ( keyCode, window ) ;
  823.             break ;
  824.         }
  825.  
  826.         default:
  827.         {
  828.             WEKey ( key, event -> modifiers, GetWindowWE (window) ) ;
  829.             break ;
  830.         }
  831.     }
  832. }
  833.  
  834. void DoUpdate ( WindowPtr window )
  835. {
  836.     GrafPtr        savePort ;
  837.     RgnHandle    updateRgn ;
  838.  
  839.     // if we have no windows, there's nothing to update!
  840.     if ( window == nil )
  841.     {
  842.         return ;
  843.     }
  844.  
  845.     // save the old drawing port
  846.     GetPort ( & savePort ) ;
  847.     SetPort ( window ) ;
  848.  
  849.     // notify everything that we're doing an update.
  850.     BeginUpdate ( window ) ;
  851.  
  852.     // BeginUpdate sets the window port visRgn to the region to update
  853.     updateRgn = window -> visRgn ;
  854.  
  855.     if ( ! EmptyRgn ( updateRgn ) )    // if it's not an empty region, let's update it!
  856.     {
  857.         // erase the update region
  858.         EraseRgn ( updateRgn ) ;
  859.  
  860.         //    draw scroll bars
  861.         UpdateControls ( window, updateRgn ) ;
  862.  
  863.         //    draw grow icon
  864.         MyDrawGrowIcon ( window, false ) ;
  865.  
  866.         //    draw text
  867.         WEUpdate ( updateRgn, GetWindowWE ( window ) ) ;
  868.     }
  869.  
  870.     // tell everything we're done updating
  871.     EndUpdate ( window ) ;
  872.  
  873.     // restore the old graphics port
  874.     SetPort ( savePort ) ;
  875. }
  876.  
  877. void DoActivate ( Boolean isActivating, WindowPtr window )
  878. {
  879.     DocumentHandle        hDocument ;
  880.     WEReference            we ;
  881.     GrafPtr                savePort ;
  882.     Rect                barRect ;
  883.     ControlPartCode        barHilite ;
  884.     SInt16                menuID ;
  885.     Orientation            orientation ;
  886.  
  887.     // if this is not one of our document windows, nothing to do here...
  888.     if ( ( hDocument = GetWindowDocument ( window ) ) == nil )
  889.     {
  890.         return ;
  891.     }
  892.     we = ( * hDocument ) -> we ;
  893.  
  894.     //    sanity check: do nothing if required activation state
  895.     //    is the same as the current activation state
  896.     if ( isActivating == WEIsActive ( we ) )
  897.     {
  898.         return ;
  899.     }
  900.  
  901.     //     set up the port
  902.     GetPort ( & savePort ) ;
  903.     SetPort ( window ) ;
  904.  
  905.     // activate or deactivate the text (and any other relevant stuff) depending on just
  906.     // what we're doing here...
  907.     if ( isActivating )
  908.     {
  909.         WEActivate ( we ) ;
  910.         barHilite = kControlNoPart ;
  911.     }
  912.     else
  913.     {
  914.         WEDeactivate ( we ) ;
  915.         barHilite = kControlDisabledPart ;
  916.     }
  917.  
  918.     //    redraw the grow icon (and validate its rect)
  919.     MyDrawGrowIcon ( window, true ) ;
  920.  
  921.     //    redraw the scroll bars with the new highlighting (and validate their rects)
  922.     for ( orientation = kVertical ; orientation <= kHorizontal ; orientation ++ )
  923.     {
  924.         HiliteControl ( ( * hDocument ) -> scrollBars [ orientation ], barHilite ) ;
  925.         CalcScrollBarRect ( window, orientation, & barRect ) ;
  926.         ValidRect ( & barRect ) ;
  927.     }
  928.  
  929.     //    if activating, undim text-related menus
  930.     if ( isActivating )
  931.     {
  932.         for ( menuID = kMenuEdit ; menuID <= kMenuFeatures ; menuID ++ )
  933.         {
  934.             EnableItem ( GetMenuHandle ( menuID ), 0 ) ;
  935.         }
  936.     }
  937.  
  938.     // invalidate the menu bar
  939.     InvalMenuBar ( ) ;
  940.  
  941.     // restore the old graphics port..
  942.     SetPort ( savePort ) ;
  943. }
  944.  
  945. OSErr CreateWindow ( const FSSpec * pFileSpec )
  946. {
  947.     DocumentHandle    hDocument = nil ;
  948.     WindowPtr        window = nil ;
  949.     AliasHandle        alias = nil ;
  950.     WEReference        we = nil ;
  951.     ControlHandle    bar = nil ;
  952.     FInfo            fileInfo ;
  953.     Rect            textRect ;
  954.     LongRect        lr ;
  955.     Orientation        orientation ;
  956.     OSErr            err ;
  957.  
  958. #ifdef __cplusplus
  959.     static WEScrollUPP            sScrollerUPP = NewWEScrollProc ( TextScrolled ) ;
  960.     static WEPreTrackDragUPP    sPreTrackerUPP = NewWEPreTrackDragProc ( AddClippingName ) ;
  961. #else
  962.     static WEScrollUPP            sScrollerUPP = nil ;
  963.     static WEPreTrackDragUPP    sPreTrackerUPP = nil ;
  964.     if ( sScrollerUPP == nil )
  965.     {
  966.         sScrollerUPP = NewWEScrollProc ( TextScrolled ) ;
  967.         sPreTrackerUPP = NewWEPreTrackDragProc ( AddClippingName ) ;
  968.     }
  969. #endif
  970.  
  971.     //    allocate a relocateable block to hold a document record
  972.     hDocument = ( DocumentHandle ) NewHandleClear ( sizeof ( DocumentRecord ) ) ;
  973.     if ( ( err = MemError( ) ) != noErr )
  974.     {
  975.         goto cleanup ;
  976.     }
  977.  
  978.     //    create the window from a 'WIND' template: the window is initially invisible
  979.     //    if ColorQuickDraw is available, create a color window
  980.     if ( gHasColorQD )
  981.     {
  982.         window = GetNewCWindow ( kWindowTemplateID, nil, ( WindowPtr ) -1L ) ;
  983.     }
  984.     else
  985.     {
  986.         window = GetNewWindow ( kWindowTemplateID, nil, ( WindowPtr ) -1L ) ;
  987.     }
  988.  
  989.     //    make sure we got a window
  990.     if ( window == nil )
  991.     {
  992.         err = memFullErr ;
  993.         goto cleanup ;
  994.     }
  995.  
  996.     // link the document record to the window and the other way around
  997.     SetWRefCon ( window, ( SInt32 ) hDocument ) ;
  998.     ( * hDocument ) -> owner = window ;
  999.  
  1000.     // we got a window, so tell QuickDraw where to draw...
  1001.     SetPort ( window ) ;
  1002.  
  1003.     //    calculate the text rectangle
  1004.     CalcTextRect ( window, & textRect ) ;
  1005.     WERectToLongRect ( & textRect, & lr ) ;
  1006.  
  1007.     //    create a new WASTE instance
  1008.     if ( ( err = WENew ( & lr, & lr, weDoAutoScroll +
  1009.                                      weDoOutlineHilite +
  1010.                                      weDoUndo +
  1011.                                      weDoIntCutAndPaste +
  1012.                                      weDoDragAndDrop +
  1013.                                      weDoUseTempMem +
  1014.                                      weDoDrawOffscreen, & we) ) != noErr )
  1015.     {
  1016.         goto cleanup ;
  1017.     }
  1018.  
  1019.     //    save a reference to the window in the WE instance
  1020.     if ( ( err = WESetInfo ( weRefCon, & window, we ) ) != noErr )
  1021.     {
  1022.         goto cleanup ;
  1023.     }
  1024.  
  1025.     //    now the other way around:  save the WE reference in the document record
  1026.     ( * hDocument ) -> we = we ;
  1027.  
  1028.     //    set up our scroll callback
  1029.     if ( ( err = WESetInfo ( weScrollProc, & sScrollerUPP, we ) ) != noErr )
  1030.     {
  1031.         goto cleanup ;
  1032.     }
  1033.  
  1034.     //    set up our pre-TrackDrag callback
  1035.     if ( ( err = WESetInfo ( wePreTrackDragHook, & sPreTrackerUPP, we ) ) != noErr )
  1036.     {
  1037.         goto cleanup ;
  1038.     }
  1039.  
  1040.     //    create the scroll bars from a control template
  1041.     for ( orientation = kVertical ; orientation <= kHorizontal; orientation ++ )
  1042.     {
  1043.         if ( ( bar = GetNewControl ( kScrollBarTemplateID, window ) ) == nil )
  1044.         {
  1045.             err = memFullErr ;
  1046.             goto cleanup ;
  1047.         }
  1048.         HiliteControl ( bar, kControlDisabledPart ) ;
  1049.  
  1050.         //    attach a LongControl record to the scroll bar:  this allows us to use long
  1051.         //    settings and thus scroll text taller than 32,767 pixels
  1052.         if ( ( err = LCAttach ( bar ) ) != noErr )
  1053.         {
  1054.             goto cleanup;
  1055.         }
  1056.  
  1057.         //    save control handle in the document record
  1058.         ( * hDocument ) -> scrollBars [ orientation ] = bar ;
  1059.  
  1060.     }    // for
  1061.  
  1062.     //    ViewChanged adjusts the scroll bars rectangles to the window frame
  1063.     ViewChanged ( window ) ;
  1064.  
  1065.     //    if pFileSpec is not nil, it points to a file to read, so let's read it!
  1066.     if ( pFileSpec != nil )
  1067.     {
  1068.         // turn the cursor into a wristwatch because this can be a lengthy operation
  1069.         SetCursor ( * GetCursor ( watchCursor ) ) ;
  1070.  
  1071.         //    retrieve file infomation
  1072.         if ( ( err = FSpGetFInfo ( pFileSpec, & fileInfo ) ) != noErr )
  1073.         {
  1074.             goto cleanup ;
  1075.         }
  1076.  
  1077.         //    make sure we recognize the file type
  1078.         if ( ( fileInfo . fdType != kTypeText ) && ( fileInfo . fdType != ftSimpleTextDocument ) )
  1079.         {
  1080.             err = badFileFormat ;
  1081.             goto cleanup ;
  1082.         }
  1083.  
  1084.         //    read in the file
  1085.         if ( ( err = ReadTextFile ( pFileSpec, we ) ) != noErr )
  1086.         {
  1087.             goto cleanup ;
  1088.         }
  1089.  
  1090.         //    set the window title to the file name
  1091.         SetWTitle ( window, pFileSpec -> name ) ;
  1092.  
  1093.         //    create an alias to keep track of the file
  1094.         if ( ( err = NewAlias ( nil, pFileSpec, & alias ) ) != noErr )
  1095.         {
  1096.             goto cleanup ;
  1097.         }
  1098.         ( * hDocument ) -> fileAlias = ( Handle ) alias ;
  1099.  
  1100.         //    if the file is a read-only file, go ahead and enable those flags
  1101.         if ( fileInfo . fdType == ftSimpleTextDocument )
  1102.         {
  1103.             WEFeatureFlag ( weFReadOnly, weBitSet, we ) ;
  1104.         }
  1105.  
  1106.         //    let's make sure the cursor is happy...
  1107.         SetCursor ( & qd . arrow ) ;
  1108.     }
  1109.  
  1110.     //    adjust scroll bar settings based on the total text height
  1111.     AdjustBars ( window ) ;
  1112.  
  1113.     //    finally!  show the document window
  1114.     ShowWindow ( window ) ;
  1115.  
  1116. cleanup:
  1117.     if ( err != noErr )
  1118.     {
  1119.         ErrorAlert ( err ) ;
  1120.     }
  1121.     return err ;
  1122. }
  1123.  
  1124. void DestroyWindow ( WindowPtr window )
  1125. {
  1126.     DocumentHandle    hDocument ;
  1127.     SInt16            menuID ;
  1128.  
  1129.     hDocument = GetWindowDocument ( window ) ;
  1130.  
  1131.     //    destroy the WASTE instance
  1132.     WEDispose ( ( * hDocument ) -> we ) ;
  1133.  
  1134.     //    destory the LongControl records attached to the scroll bars
  1135.     LCDetach ( ( * hDocument ) -> scrollBars [ kVertical ] ) ;
  1136.     LCDetach ( ( * hDocument ) -> scrollBars [ kHorizontal ] ) ;
  1137.  
  1138.     //    dispose of the file alias, if any
  1139.     ForgetHandle ( & ( ( * hDocument ) -> fileAlias ) ) ;
  1140.  
  1141.     //    destroy the window record and all associated data structures
  1142.     DisposeWindow ( window ) ;
  1143.  
  1144.     //    finally, dispose of the document record
  1145.     DisposeHandle ( ( Handle ) hDocument ) ;
  1146.  
  1147.     // adjust the menus to suit
  1148.     for ( menuID = kMenuFont ; menuID <= kMenuFeatures ; menuID ++ )
  1149.     {
  1150.         DisableItem ( GetMenuHandle ( menuID ), 0 ) ;
  1151.     }
  1152.     InvalMenuBar ( ) ;
  1153. }
  1154.